package com.ty.user.starter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ty.user.starter.constants.Constant;
import com.ty.user.starter.entity.Permission;
import com.ty.user.starter.entity.Role;
import com.ty.user.starter.entity.criteria.RoleCriteria;
import com.ty.user.starter.entity.dto.RoleDto;
import com.ty.user.starter.entity.vo.RoleVo;
import com.ty.user.starter.mapper.MenuMapper;
import com.ty.user.starter.mapper.PermissionMapper;
import com.ty.user.starter.mapper.RoleMapper;
import com.ty.user.starter.service.RoleService;
import com.ty.user.starter.util.Pinyin4jUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author minku
 * @since 2021-10-16
 */
@Slf4j
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Resource
    private PermissionMapper permissionMapper;
    @Resource
    private MenuMapper menuMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(RoleDto dto) {
        Assert.isNull(dto.getId(), Constant.ID_IS_NULL);
        this.validName(null, dto.getName());

        Role role = new Role();
        BeanUtils.copyProperties(dto, role);
        role.setBusCode(Pinyin4jUtil.getPinYinHeadChar(role.getName()));
        baseMapper.insert(role);
        this.batchAddPerms(role, dto.getMenuIds());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(RoleDto dto) {
        Assert.notNull(dto.getId(), Constant.ID_NOT_NULL);
        this.validName(dto.getId(), dto.getName());

        Role role = get(dto.getId());
        BeanUtils.copyProperties(dto, role);
        role.setBusCode(Pinyin4jUtil.getPinYinHeadChar(role.getName()));
        baseMapper.updateById(role);
        this.batchAddPerms(role, dto.getMenuIds());
    }

    /**
     * 批量设置角色的权限菜单
     *
     * @param role    角色信息
     * @param menuIds 权限菜单列表
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchAddPerms(@NotNull Role role, List<Long> menuIds) {
        // 删除旧的权限菜单列表
        QueryWrapper<Permission> delQuery = new QueryWrapper<>();
        delQuery.eq("role_id", role.getId());
        permissionMapper.delete(delQuery);
        // 如果有设置权限菜单则进行批量插入操作
        if (!CollectionUtils.isEmpty(menuIds)) {
            // 验证权限菜单是否重复设置
            long count = menuIds.parallelStream().distinct().count();
            Assert.isTrue(menuIds.size() == count, "权限菜单不能重复设置");
            // 验证权限菜单的有效性
            menuIds.forEach(menuId -> Assert.notNull(menuMapper.selectById(menuId), String.format("菜单id: %d 不存在", menuId)));
            // 批量插入
            menuIds.forEach(menuId -> {
                Permission permission = new Permission();
                permission.setRoleId(role.getId());
                permission.setMenuId(menuId);
                permissionMapper.insert(permission);
            });
        }
    }

    private Role get(long id) {
        Role role = baseMapper.selectById(id);
        Assert.notNull(role, "角色信息不存在");
        return role;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(long id) {
        this.get(id);
        // 先删除权限表中的角色权限列表,再删除角色
        Map<String, Object> condition = new ConcurrentHashMap<>();
        condition.put("role_id", id);
        permissionMapper.deleteByMap(condition);
        baseMapper.deleteById(id);
    }

    @Override
    public IPage<RoleVo> findAll(RoleCriteria criteria) {
        return baseMapper.findAll(criteria.defaultPage(), criteria);
    }

    @Override
    public List<Long> getMenus(long roleId) {
        return baseMapper.findById(roleId);
    }

    /**
     * 校验角色名称是否重复
     *
     * @param id   角色id
     * @param name 名称名称
     */
    private void validName(Long id, @NonNull String name) {
        QueryWrapper<Role> query = new QueryWrapper<>();
        query.eq("name", name);
        if (null != id) {
            query.ne("id", id);
        }
        Assert.isTrue(baseMapper.selectCount(query) == 0, "角色名称已存在");
    }
}
